package cn.com.dhcc.app.pub.core.util;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.log4j.Logger;

import cn.com.dhcc.app.core.exception.ServiceException;
import cn.com.dhcc.app.core.handler.threadpoolhandler.ThreadPool;
import cn.com.dhcc.app.core.service.Log;
import cn.com.dhcc.app.core.service.Log.LOGTYPE;
import cn.com.dhcc.app.core.util.FileUtil;
import cn.com.dhcc.app.core.util.XstreamUtil;


/**
 * 
 * 
 * 注意点：
 * 1.两个线程执行的代码片段要实现同步互斥的效果，它们必须用同一个Lock对象。也就是必须是同一个ReentrantReadWriteLock对象
 * 2.ReentrantReadWriteLock会使用两把锁来解决问题，一个读锁，一个写锁
 *   线程进入读锁的前提条件：1）没有其他线程的写锁，2）没有写请求或者有写请求，但调用线程和持有锁的线程是同一个 也就是说一个线程加了写锁，该线程还能获得读锁，这种方式成为锁的降级。但是如果一个线程加了写锁，另一个线程是不允许加读锁的
 *   线程进入写锁的前提条件：1）没有其他线程的读锁   2）没有其他线程的写锁
 *   一句话概括就是：一个线程加了读锁，那么另一个线程就不能写，但是另一个线程可以读。 如果一个线程加了写锁，那么任何的读写操作都不允许
 * 3.ReadLock可以被多个线程持有并且在作用时排斥任何的WriteLock，而WriteLock则是完全的互斥。
 *    这一特性最为重要，因为对于高读取频率而相对较低写入的数据结构，使用此类锁同步机制则可以提高并发量。
 * 
 * 
 * 文件加锁删除
 * 
 * @日期 2015-11-13 下午3:48:40
 * @作者  高强
 */
public class LockFileUtil {
	@SuppressWarnings("deprecation")
	private static Logger logger = Log.getDetachLogger(LOGTYPE.QUARTZJOB);
	
	
	/**
	 * 文件删除
	 * @param file
	 * @param preLastM
	 * @return
	 */
	public static boolean delete(File file,long preLastM){
		LockFile lockFile=new LockFile(file.getAbsolutePath());
		lockFile.getLock().writeLock().lock();//得到一个写的锁
		logger.info("文件将要删除，【"+file.getAbsolutePath()+"】加上了一个写锁");
		long last=file.lastModified();
		boolean flag=false;
		if (preLastM==last) {
			flag=file.delete();
			logger.info("文件【"+file.getAbsolutePath()+"】加锁删除结果"+flag+"，释放锁");
		}else{
			logger.info("文件【"+file.getAbsolutePath()+"】已经更新了，不需要删除"+",释放锁");
		}
		lockFile.getLock().writeLock().unlock();//得到一个写的锁
		return flag;
	}
	
	/**
	 * 加锁写入
	 * @param file
	 * @param content
	 */
	public static void write(File file,String content){
		LockFile lockFile=new LockFile(file.getAbsolutePath());
		lockFile.getLock().writeLock().lock();//得到一个写的锁
		logger.info("文件将写数据，【"+file.getAbsolutePath()+"】加上了一个写锁");
		try {
			FileUtil.write(file, content);
		} catch (IOException e) {
			logger.error("文件将写数据报错:"+e);
		}finally{
			logger.info("文件写完数据，【"+file.getAbsolutePath()+"】释放锁");
			lockFile.getLock().writeLock().unlock();//得到一个写的锁
		}
	}
	
	/**
	 * 文件写
	 */
	public static void toXmlfile(Object fromObj, File xmlfile){
		LockFile lockFile=new LockFile(xmlfile.getAbsolutePath());
		lockFile.getLock().writeLock().lock();//得到一个写的锁
		logger.info("文件将写数据，【"+xmlfile.getAbsolutePath()+"】加上了一个写锁");
		try {
			XstreamUtil.toXmlfile(fromObj, xmlfile);
		} catch (IOException e) {
			logger.error("文件将写数据报错:"+e);
		}finally{
			logger.info("文件写完数据，【"+xmlfile.getAbsolutePath()+"】释放锁");
			lockFile.getLock().writeLock().unlock();//得到一个写的锁
		}
	}
	
}
class LockFile extends File{
	@SuppressWarnings("deprecation")
	private static Logger logger = Log.getDetachLogger(LOGTYPE.QUARTZJOB);
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static Map<String , LockInfo> locks = new ConcurrentHashMap<String , LockInfo>();
	private final static int CLEAR_MINUS=60;//默认锁的清理时间是60分钟，单位是分钟
	private ReentrantReadWriteLock lock = null;
	
	static{
		ThreadPool.createPool("LOCKS", 1, 1, Integer.MAX_VALUE);
		try {
			/**如果锁不清理，那么就会造成锁越来越多，需要把没用的锁给清理掉
			 * 清理锁的线程池非常重要，也很危险，因为对于一个线程而言，如果一个线程加了锁，那么这个锁如果清理了那么此时如果另一个线程要想与该线程同步的话，就锁不住了，因为这个锁已经被清理掉了
			 * 因此，一定要等到线程同步执行完后，清理才是最合适的时机
			 * 
			 * 锁的清理机制，创建锁的时候，记录创建锁的时间，清理的时候只清理创建时间距离当前时间一小时的锁
			 */
			ThreadPool.execute("LOCKS", new Runnable() {
				@Override
				public void run() {
					while (true) {
						Date c=new Date();//当前时间
						logger.info("线程池LOCKS开始清理创建时间大于"+CLEAR_MINUS+"分钟的锁 ，清理之前锁的数量:"+locks.size());
						try {
							Set<String> keys=locks.keySet();
							if (keys!=null && !keys.isEmpty()) {
								for (String key:keys) {
									LockInfo li=locks.get(key);
									if (li!=null && li.getCreated()!=null) {
										if (c.getTime()-li.getCreated().getTime()>CLEAR_MINUS*60*1000) {//如果创建的时间超过1小时
											logger.warn("线程池LOCKS清理锁"+key+"，该锁创建了"+(c.getTime()-li.getCreated().getTime())/1000+"秒");
											locks.remove(key);
										}
									}
									
								}
							}
							Thread.sleep(1*60*60*1000);//休眠1小时
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						logger.info("线程池LOCKS清理完成创建时间大于"+CLEAR_MINUS+"分钟的锁 ，清理完成后锁的数量:"+locks.size());
						
					}
				}
			});
		} catch (ServiceException e) {
			logger.error("线程池LOCKS清理大于1小时的锁失败:"+e);
		}
	}
	public LockFile(File arg0, String arg1) {
		super(arg0, arg1);
		lock = initLock(this.getAbsolutePath());
	}

	public LockFile(String arg0, String arg1) {
		super(arg0, arg1);
		lock = initLock(this.getAbsolutePath());
	}

	public LockFile(String arg0) {
		super(arg0);
		lock = initLock(this.getAbsolutePath());
	}

	public LockFile(URI arg0) {
		super(arg0);
		lock = initLock(this.getAbsolutePath());
	}
	
	/*
	 * 这里要注意使用 static synchronized，不然可能同时有多个进行初始化这个文件
	 */
	private static synchronized ReentrantReadWriteLock initLock(String path) {
		ReentrantReadWriteLock lock = locks.get(path)==null ? null:locks.get(path).getReentrantReadWriteLock();
		if (lock == null) {
			lock = new ReentrantReadWriteLock();
			locks.put(path, new LockInfo(lock,new Date()));//
		}
		return lock;
	}
	
	public ReentrantReadWriteLock getLock() {
		while (lock==null) {
			initLock(this.getAbsolutePath());
		}
		return lock;
	}
}

class LockInfo {
	private ReentrantReadWriteLock ReentrantReadWriteLock;
	private Date created;//锁的创建时间
	public ReentrantReadWriteLock getReentrantReadWriteLock() {
		return ReentrantReadWriteLock;
	}
	public void setReentrantReadWriteLock(
			ReentrantReadWriteLock reentrantReadWriteLock) {
		ReentrantReadWriteLock = reentrantReadWriteLock;
	}
	public Date getCreated() {
		return created;
	}
	public void setCreated(Date created) {
		this.created = created;
	}
	public LockInfo(
			java.util.concurrent.locks.ReentrantReadWriteLock reentrantReadWriteLock,
			Date created) {
		super();
		ReentrantReadWriteLock = reentrantReadWriteLock;
		this.created = created;
	}
	public LockInfo() {
		super();
		// TODO Auto-generated constructor stub
	}
	
	
	
}
	